// audio2wav.cpp : Defines the entry point for the DLL application.
//

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>  /* for fstat routine */
#include <fcntl.h>
//#include <io.h>
#include <limits.h>
#include <errno.h>
#include <math.h> // for pow() and log10()
#include "bstdfile.h"
#include "mad.h"
#include "audio2wav.h"

#ifdef _WIN32
int _snprintf(char *szBuf, int nBuf, const char *szFmt, ...);
#else
#define _snprintf snprintf
#endif  // _WIN32

#ifdef _WIN32
BOOL APIENTRY DllMain( HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved )
{
	switch(ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		DisableThreadLibraryCalls(hModule);
		break;
	default:
		break;
	}
    return TRUE;
}
#endif  // _WIN32

#if (MAD_VERSION_MAJOR>=1) || \
    ((MAD_VERSION_MAJOR==0) && \
     (((MAD_VERSION_MINOR==14) && \
       (MAD_VERSION_PATCH>=2)) || \
      (MAD_VERSION_MINOR>14)))
#define MadErrorString(x) mad_stream_errorstr(x)
#else
static const char *MadErrorString(const struct mad_stream *Stream)
{
	switch(Stream->error)
	{
		// Generic unrecoverable errors.
		case MAD_ERROR_BUFLEN:
			return("input buffer too small (or EOF)");
		case MAD_ERROR_BUFPTR:
			return("invalid (null) buffer pointer");
		case MAD_ERROR_NOMEM:
			return("not enough memory");
		// Frame header related unrecoverable errors.
		case MAD_ERROR_LOSTSYNC:
			return("lost synchronization");
		case MAD_ERROR_BADLAYER:
			return("reserved header layer value");
		case MAD_ERROR_BADBITRATE:
			return("forbidden bitrate value");
		case MAD_ERROR_BADSAMPLERATE:
			return("reserved sample frequency value");
		case MAD_ERROR_BADEMPHASIS:
			return("reserved emphasis value");
		// Recoverable errors
		case MAD_ERROR_BADCRC:
			return("CRC check failed");
		case MAD_ERROR_BADBITALLOC:
			return("forbidden bit allocation value");
		case MAD_ERROR_BADSCALEFACTOR:
			return("bad scalefactor index");
		case MAD_ERROR_BADFRAMELEN:
			return("bad frame length");
		case MAD_ERROR_BADBIGVALUES:
			return("bad big_values count");
		case MAD_ERROR_BADBLOCKTYPE:
			return("reserved block_type");
		case MAD_ERROR_BADSCFSI:
			return("bad scalefactor selection info");
		case MAD_ERROR_BADDATAPTR:
			return("bad main_data_begin pointer");
		case MAD_ERROR_BADPART3LEN:
			return("bad audio data length");
		case MAD_ERROR_BADHUFFTABLE:
			return("bad Huffman table select");
		case MAD_ERROR_BADHUFFDATA:
			return("Huffman data overrun");
		case MAD_ERROR_BADSTEREO:
			return("incompatible block_type for JS");
		// Unknown error. This switch may be out of sync with libmad's defined error codes.
		default:
			return("Unknown error code");
	}
}
#endif


/****************************************************************************
 * 'Borrowed' from audio.c in the madplay source
 * NAME:	Prng()
 * DESCRIPTION:	32-bit pseudo-random number generator
 ****************************************************************************/
static inline unsigned long Prng(unsigned long state)
{
	return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL;
}


/****************************************************************************
 * A little more advanced scaling routine based on madplay's 
 * audio_linear_dither() function. Rather than simply rounding the 24 bit
 * number down to 16 bits (as MadFixedToSshort() in madlld does), it performs 
 * dithering, which is the addition of a random number to the least 
 * significant bits (LSB) of the sample that targets the LSB at the 16 bit
 * mark. CbMpegAudioDecoder() uses this to perform its scaling before output.
 ****************************************************************************/
static inline signed short Scale(mad_fixed_t sample, mad_fixed_t *gRandom)
{
	unsigned int scalebits;
	mad_fixed_t output, mask, rnd;

	/* bias */
	output = sample + (1L << (MAD_F_FRACBITS + 1 - 16 - 1));	
	
  	scalebits = MAD_F_FRACBITS + 1 - 16;
  	mask = (mad_fixed_t)( (1L << scalebits) - 1 );

  	/* dither */
	rnd = (mad_fixed_t)Prng(*gRandom);
  	output += (rnd & mask) - (*gRandom & mask);
	*gRandom = rnd;
	
  	/* clip */
	if (output >= MAD_F_ONE)
    	output = MAD_F_ONE - 1;
	else if (output < -MAD_F_ONE)
    	output = -MAD_F_ONE;

  	/* quantize */
  	output &= ~mask;

	/* scale */
	return output >> scalebits;
}

/****************************************************************************
 * This is based on the MpegAudioDecoder() function in the madlld source.
 * It differs in that it performs the file opens, taking the path/filenames 
 * as arguments, and passes the following information to a callback 
 * function while in the decoding loop, to be handled by the calling code 
 * (generally used for reporting--see test.cpp for an example):
 *
 *			Frame count			
 *			Total input bytes read
 *			A pointer to a mad_header struct (MP3 info)
 *
 * Since this is used as a library function, errors and information are 
 * reported back to a char pointer (StatMsg) passed as a parameter from the 
 * calling code.
 * 
 * It also uses a flag (WavPad) passed from the calling code which tells it
 * whether to add a RIFF/WAVE header (making OutFN a WAV file). If WavPad!=0
 * then 44 null bytes are written to the beginning of OutFN before decoding 
 * occurs, and the WAV header is constructed and added after decoding.
 ****************************************************************************/
#define INPUT_BUFFER_SIZE	(5*8192)
#define OUTPUT_BUFFER_SIZE	8192 // Must be an integer multiple of 4.

class IOutput
{
public:
    virtual int onDataBegin(int nChannel, int nSampleRate) = 0;
    virtual int output(unsigned char *pBuf, int nSize) = 0;
    virtual int onFrameEnd(unsigned int nFrame, mad_timer_t *pTime) = 0;
    virtual int onDataEnd(unsigned int nBytes) = 0;
};

/** context for mp3 decoding
 */
struct SDecodeContext
{
public:
	/// input .mp3 file
	FILE	*InputFp;
	/// output .wav/.raw file
	IOutput	*m_pOutput;
	/// callback function, may be NULL
	LPMadCallback	mCallback;
	/// input buffer
	unsigned char InputBuffer[INPUT_BUFFER_SIZE + MAD_BUFFER_GUARD];
	/// output buffer
	unsigned char OutputBuffer[OUTPUT_BUFFER_SIZE];

public:
	// to decode mp3 file into .wav file(or .raw file)
	int decode(char *lpError, int nBufSize);
};

int SDecodeContext::decode(char *lpError, int nBufSize)
{
	struct mad_stream	Stream;
	struct mad_frame	Frame;
	struct mad_synth	synth;
	mad_timer_t			Timer;
	unsigned char *OutputPtr = OutputBuffer;
	unsigned char *GuardPtr = NULL;
	const unsigned char	*OutputBufferEnd = OutputBuffer + OUTPUT_BUFFER_SIZE;
	int	i, Status = 0;
	unsigned int	FrameCount=0;
    int nSampleRate = 0;
	unsigned int	ByteCount=0;// count total input bytes
    unsigned int    nOutputByteCount = 0;
	bstdfile_t	*BstdFile;

	if (lpError != NULL)
		*lpError = '\0';
	// Random number for generating dither. Used by Scale() below in preparing the samples for 16 bit output. 
	mad_fixed_t gRand = 0xa8b9ff7e;
	// First the structures used by libmad must be initialized.
	mad_stream_init(&Stream);
	mad_frame_init(&Frame);
	mad_synth_init(&synth);
	mad_timer_reset(&Timer);
	/* Decoding options can here be set in the options field of the
	 * Stream structure.
	 */
	/* {1} When decoding from a file we need to know when the end of
	 * the file is reached at the same time as the last bytes are read
	 * (see also the comment marked {3} bellow). Neither the standard
	 * C fread() function nor the POSIX read() system call provides
	 * this feature. We thus need to perform our reads through an
	 * interface having this feature, this is implemented here by the
	 * bstdfile.c module.
	 */
	if ((BstdFile=NewBstdFile(InputFp)) == NULL) {
		if (lpError != NULL)
			_snprintf(lpError, nBufSize, "Can't create a new bstdfile_t (%s).\n", strerror(errno));
		return 1;
	}
	// This is the decoding loop.
    if (mCallback != NULL)
        mCallback(0, 0, NULL);
	do{
		// The input bucket must be filled if it becomes empty or
		// if it's the first execution of the loop.
		if(Stream.buffer==NULL || Stream.error==MAD_ERROR_BUFLEN)
		{
			size_t	ReadSize, Remaining;
			unsigned char	*ReadStart;
			/* {2} libmad may not consume all bytes of the input
			 * buffer. If the last frame in the buffer is not wholly
			 * contained by it, then that frame's start is pointed by
			 * the next_frame member of the Stream structure. This
			 * common situation occurs when mad_frame_decode() fails,
			 * sets the stream error code to MAD_ERROR_BUFLEN, and
			 * sets the next_frame pointer to a non NULL value. (See
			 * also the comment marked {4} bellow.)
			 *
			 * When this occurs, the remaining unused bytes must be
			 * put back at the beginning of the buffer and taken in
			 * account before refilling the buffer. This means that
			 * the input buffer must be large enough to hold a whole
			 * frame at the highest observable bit-rate (currently 448
			 * kb/s). XXX=XXX Is 2016 bytes the size of the largest
			 * frame? (448000*(1152/32000))/8
			 */
			if (Stream.next_frame != NULL) {
				Remaining = Stream.bufend - Stream.next_frame;
				memmove(InputBuffer, Stream.next_frame, Remaining);
				ReadStart = InputBuffer + Remaining;
				ReadSize = INPUT_BUFFER_SIZE - Remaining;
			} else {
				ReadSize = INPUT_BUFFER_SIZE;
				ReadStart = InputBuffer;
				Remaining = 0;
			}
			/* Fill-in the buffer. If an error occurs print a message
			 * and leave the decoding loop. If the end of stream is
			 * reached we also leave the loop but the return status is
			 * left untouched.
			 */
			ReadSize = BstdRead(ReadStart,1,ReadSize,BstdFile);
			ByteCount += ReadSize;		// passed to CbFunc
			if (ReadSize <= 0) {
				if (ferror(InputFp)) {
					if (lpError != NULL)
						_snprintf(lpError, nBufSize, "Read error on bit-stream (%s)\n", strerror(errno));
					Status = 1;
				}
				if (feof(InputFp) && lpError!=NULL)
					_snprintf(lpError, nBufSize, "End of input stream\n"); 
				break;
			}
			/* {3} When decoding the last frame of a file, it must be
			 * followed by MAD_BUFFER_GUARD zero bytes if one wants to
			 * decode that last frame. When the end of file is
			 * detected we append that quantity of bytes at the end of
			 * the available data. Note that the buffer can't overflow
			 * as the guard size was allocated but not used the the
			 * buffer management code. (See also the comment marked
			 * {1}.)
			 *
			 * In a message to the mad-dev mailing list on May 29th,
			 * 2001, Rob Leslie explains the guard zone as follows:
			 *
			 *    "The reason for MAD_BUFFER_GUARD has to do with the
			 *    way decoding is performed. In Layer III, Huffman
			 *    decoding may inadvertently read a few bytes beyond
			 *    the end of the buffer in the case of certain invalid
			 *    input. This is not detected until after the fact. To
			 *    prevent this from causing problems, and also to
			 *    ensure the next frame's main_data_begin pointer is
			 *    always accessible, MAD requires MAD_BUFFER_GUARD
			 *    (currently 8) bytes to be present in the buffer past
			 *    the end of the current frame in order to decode the
			 *    frame."
			 */
			if (BstdFileEofP(BstdFile)) {
				GuardPtr = ReadStart + ReadSize;
				memset(GuardPtr, 0, MAD_BUFFER_GUARD);
				ReadSize += MAD_BUFFER_GUARD;
			}
			// Pipe the new buffer content to libmad's stream decoder facility.
			mad_stream_buffer(&Stream, InputBuffer, (unsigned int)(ReadSize + Remaining));
			Stream.error = (mad_error)0;
		}
		/* Decode the next MPEG frame. The streams is read from the
		 * buffer, its constituents are break down and stored the the
		 * Frame structure, ready for examination/alteration or PCM
		 * synthesis. Decoding options are carried in the Frame
		 * structure from the Stream structure.
		 *
		 * Error handling: mad_frame_decode() returns a non zero value
		 * when an error occurs. The error condition can be checked in
		 * the error member of the Stream structure. A mad error is
		 * recoverable or fatal, the error status is checked with the
		 * MAD_RECOVERABLE macro.
		 *
		 * {4} When a fatal error is encountered all decoding
		 * activities shall be stopped, except when a MAD_ERROR_BUFLEN
		 * is signaled. This condition means that the
		 * mad_frame_decode() function needs more input to complete
		 * its work. One shall refill the buffer and repeat the
		 * mad_frame_decode() call. Some bytes may be left unused at
		 * the end of the buffer if those bytes forms an incomplete
		 * frame. Before refilling, the remaining bytes must be moved
		 * to the beginning of the buffer and used for input for the
		 * next mad_frame_decode() invocation. (See the comments
		 * marked {2} earlier for more details.)
		 *
		 * Recoverable errors are caused by malformed bit-streams, in
		 * this case one can call again mad_frame_decode() in order to
		 * skip the faulty part and re-sync to the next frame.
		 */
		if (mad_frame_decode(&Frame, &Stream) != 0) {
			if (MAD_RECOVERABLE(Stream.error)) {
				/* Do not print a message if the error is a loss of
				 * synchronization or this loss is due to the end of
				 * stream guard bytes. (See the comments marked {3}
				 * supra for more informations about guard bytes.)
				 */
				if( Stream.error!=MAD_ERROR_LOSTSYNC || Stream.this_frame!=GuardPtr ||
					// Suppress error if ID3 or TAG(??) tag was cause:
				   (Stream.this_frame[0] != 'I' && Stream.this_frame[1] !='D' && Stream.this_frame[2] !='3') ||				   
					(Stream.this_frame[0] !='T' && Stream.this_frame[1] != 'A' && Stream.this_frame[2] != 'G') )
				{
					if (lpError != NULL)
						_snprintf(lpError, nBufSize, "Recoverable frame level error (%s)\n", MadErrorString(&Stream));
				}
				continue;
			} else {
				if (Stream.error==MAD_ERROR_BUFLEN)
					continue;
				if (lpError != NULL)
					_snprintf(lpError, nBufSize, "Unrecoverable frame level error (%s).\n", MadErrorString(&Stream));
				Status = 1;
				break;
			}
		}
		/* Accounting. The computed frame duration is in the frame
		 * header structure. It is expressed as a fixed point number
		 * whole data type is mad_timer_t. It is different from the
		 * samples fixed point format and unlike it, it can't directly
		 * be added or subtracted. The timer module provides several
		 * functions to operate on such numbers. Be careful there, as
		 * some functions of libmad's timer module receive some of
		 * their mad_timer_t arguments by value!
		 */
		FrameCount++;
		mad_timer_add(&Timer, Frame.header.duration);
		// Set the channel and sample rate if WAV header
		if (!nSampleRate) {
			// assign number of channels
			nSampleRate = (int)Frame.header.samplerate;
            m_pOutput->onDataBegin(2, nSampleRate);
		}
		// Once decoded the frame is synthesized to PCM samples.
		// No errors are reported by mad_synth_frame();
		mad_synth_frame(&synth, &Frame);
		/* Synthesized samples must be converted from libmad's fixed
		 * point number to the consumer format. Here we use unsigned
		 * 16 bit big endian integers on two channels. Integer samples
		 * are temporarily stored in a buffer that is flushed when
		 * full.
		 */
		for(i=0; i<synth.pcm.length; i++) {
			signed short Sample;
			// Left channel
			Sample = Scale(synth.pcm.samples[0][i], &gRand);
			// output in 16 bit little-endian
			*(OutputPtr++) = ((Sample>>0) & 0xff);
			*(OutputPtr++) = ((Sample>>8) & 0xff);
			/* Right channel. If the decoded stream is monophonic then
			 * the right output channel is the same as the left one.
			 */
			if (MAD_NCHANNELS(&Frame.header) == 2)
				Sample = Scale(synth.pcm.samples[1][i], &gRand);
			// output in 16 bit little-endian
			*(OutputPtr++) = ((Sample>>0) & 0xff);			
			*(OutputPtr++) = ((Sample>>8) & 0xff);
			// Flush the output buffer if it is full.
			if (OutputPtr == OutputBufferEnd) {
				if (m_pOutput->output(OutputBuffer, OUTPUT_BUFFER_SIZE) != OUTPUT_BUFFER_SIZE) {
					if (lpError != NULL)
						_snprintf(lpError, nBufSize, "PCM write error (%s).\n", strerror(errno));
					Status = 2;
					break;
				}
                nOutputByteCount += OUTPUT_BUFFER_SIZE;
				OutputPtr = OutputBuffer;
			}
		}
        if (OutputPtr != OutputBuffer) {
            int nBytes = (int)(OutputPtr - OutputBuffer);
            m_pOutput->output(OutputBuffer, nBytes);
            nOutputByteCount += nBytes;
            OutputPtr = OutputBuffer;
        }
        // Send accumulated information to the callback; This gets called every loop.
        if (mCallback != NULL)
            mCallback(FrameCount, ByteCount, &Frame.header);
        m_pOutput->onFrameEnd(FrameCount, &Timer);
	}while(Status == 0);
	// The input file was completely read; the memory allocated by our reading module must be reclaimed.
	BstdFileDestroy(BstdFile);
	// Mad is no longer used, the structures that were initialized must now be cleared.
	mad_synth_finish(&synth);
	mad_frame_finish(&Frame);
	mad_stream_finish(&Stream);
	// Accounting report if no error occurred.
    m_pOutput->onDataEnd(nOutputByteCount);
	return Status;
}

/////////////////////////////////////////////////////////////////////////////////

class CSimpleOutputImp : public IOutput
{
public:
    int m_nChannel;
    int m_nSampleRate;
    
public:
    virtual int onDataBegin(int nChannel, int nSampleRate)
    {
        m_nChannel = nChannel;
        m_nSampleRate = nSampleRate;
        return 0;
    }
    virtual int output(unsigned char *pBuf, int nSize)
    {   return 0;}
    virtual int onFrameEnd(unsigned int nFrame, mad_timer_t *pTime)
    {   return 0;}
    virtual int onDataEnd(unsigned long nBytes)
    {   return 0;}
};

/////////////////////////////////////////////////////////////////////////////////

class CFileOutput : public CSimpleOutputImp
{
public:
    CFileOutput(const char *szFile, int fWaveFile);
    ~CFileOutput();
    
public:
    void close();
    
public:
    virtual int output(unsigned char *pBuf, int nSize);
    virtual int onDataEnd(unsigned int nBytes);

protected:
    const char *mFile;
    FILE    *mOutput;
    int     mWaveFile;
    int     mWaveHeader;
};

CFileOutput::CFileOutput(const char *szFile, int fWaveFile)
{
    mFile = szFile;
    mOutput = fopen(szFile, "wb");
    mWaveFile = fWaveFile;
    mWaveHeader = 0;
}

CFileOutput::~CFileOutput()
{
    close();
}

void CFileOutput::close()
{
    if (mOutput != NULL) {
        fclose(mOutput);
        mOutput = NULL;
    }
}

int CFileOutput::output(unsigned char *pBuf, int nSize)
{
    if (!mOutput)
        return 0;
    if (mWaveFile && !mWaveHeader) {
        wavhdr header;
        memset(&header, 0, sizeof(header));
        if (fwrite(mOutput, 1, sizeof(header), mOutput) != sizeof(header))
            return 0;
        mWaveHeader = 1;
    }
    return (int)fwrite(pBuf, 1, nSize, mOutput);
}

int CFileOutput::onDataEnd(unsigned int nBytes)
{
    if (nBytes==0 || !mFile || !*mFile)
        return -1;
    close();
    if (!mWaveFile)
        return 0;
    // setup header
    wavhdr wavh;
    memset(&wavh, 0, sizeof(wavh));
    wavh.nChannels = (short)m_nChannel;
    wavh.nSamplesPerSec = m_nSampleRate;
    wavh.RIFF = 0x46464952;	// 'RIFF'
    wavh.size = (int)(nBytes + sizeof(wavhdr) - 8);	// total size of wave file, including header
    wavh.WAVE = 0x45564157;	// 'WAVE'
    wavh.fmt = 0x20746D66;	// 'fmt '
    wavh.wBitsPerSample = 16;
    wavh.wFormatLength = 16;
    wavh.wFormatTag = 1;
    wavh.data = 0x61746164;	// 'data'
    wavh.dSize = (int)nBytes;
    wavh.nBlockAlign = (short)(wavh.wBitsPerSample / 8 * wavh.nChannels);
    wavh.nAvgBytesPerSec = wavh.nSamplesPerSec * wavh.nBlockAlign;
    // write wave header
    FILE *fp = fopen(mFile, "rb+");
    if (!fp)
        return 0;
    size_t nWrite = fwrite(&wavh, 1, sizeof(wavh), fp);
    fclose(fp);
    return (nWrite==sizeof(wavh) ? 1 : 0);
}

/////////////////////////////////////////////////////////////////////////////////
extern "C" AUDIO2WAV_EXPORTAPI
int __stdcall MpegDecoder(const char *szInFile, const char *szOutFile, int fWaveFile,
						  char *lpszMsg, int nMaxMsgSize, LPMadCallback CbFunc)
{
	if (!szInFile || !*szInFile || !szOutFile || !*szOutFile)
		return -1;
	FILE *InputFp;
	// Open passed files for reading and writing
	if ( (InputFp = fopen(szInFile, "rb")) == NULL) {
		if (lpszMsg != NULL)
			_snprintf(lpszMsg, nMaxMsgSize, "fail to read:%s!\n", szInFile);
		return 1;
	}
	// to use new instead of stack allocation
	SDecodeContext *pContext;
	int nResult = -1;
	if ((pContext = new SDecodeContext()) != NULL) {
        CFileOutput out(szOutFile, fWaveFile);
		pContext->InputFp = InputFp;
		pContext->m_pOutput = &out;
		pContext->mCallback = CbFunc;
		nResult = pContext->decode(lpszMsg, nMaxMsgSize);
	} else {
		if (lpszMsg != NULL)
			_snprintf(lpszMsg, nMaxMsgSize, "memory not enough:%d!\n", (int)sizeof(SDecodeContext));
	}
	// close files
	fclose(InputFp);
	delete pContext;
	return nResult;
}

////////////////////////////////////////////////////////////////////////////////

class CCallbackOutput : public CSimpleOutputImp
{
public:
    CCallbackOutput(LPDecodeCallback lpfnCallback, void *lpContext)
    {
        m_lpfnCallback = lpfnCallback;
        m_lpContext = lpContext;
    }
    
public:
    virtual int onDataBegin(int nChannel, int nSampleRate)
    {
        int nData[] = {nChannel, nSampleRate};
        m_lpfnCallback(m_lpContext, 1, (const unsigned char *)nData, sizeof(int)*2);
        return 0;
    }
    virtual int output(unsigned char *pBuf, int nSize)
    {
        m_lpfnCallback(m_lpContext, 2, pBuf, nSize);
        return nSize;
    }
    virtual int onFrameEnd(unsigned int nFrame, mad_timer_t *pTime)
    {
        unsigned int nData[] = {nFrame, (unsigned int)pTime->seconds, pTime->fraction, MAD_TIMER_RESOLUTION};
        m_lpfnCallback(m_lpContext, 3, (const unsigned char *)nData, sizeof(int)*4);
        return 0;
    }
    virtual int onDataEnd(unsigned int nBytes)
    {
        unsigned int nData[] = {nBytes};
        m_lpfnCallback(m_lpContext, 4, (const unsigned char *)nData, sizeof(int));
        return 0;
    }
    
protected:
    LPDecodeCallback m_lpfnCallback;
    void *m_lpContext;
};

extern "C" AUDIO2WAV_EXPORTAPI
int __stdcall MpegDecodeWithCallback(const char *szInFile, char *lpError, int nMaxError, LPDecodeCallback lpfnCallback, void *lpContext)
{
    if (!szInFile || !*szInFile || !lpfnCallback)
        return -1;
    FILE *InputFp;
    // Open passed files for reading and writing
    if ( (InputFp = fopen(szInFile, "rb")) == NULL) {
        if (lpError != NULL)
            _snprintf(lpError, nMaxError, "fail to read:%s!\n", szInFile);
        return 1;
    }
    // to use new instead of stack allocation
    SDecodeContext *pContext;
    int nResult = -1;
    if ((pContext = new SDecodeContext()) != NULL) {
        CCallbackOutput out(lpfnCallback, lpContext);
        pContext->InputFp = InputFp;
        pContext->m_pOutput = &out;
        pContext->mCallback = NULL;
        nResult = pContext->decode(lpError, nMaxError);
    } else {
        if (lpError != NULL)
            _snprintf(lpError, nMaxError, "memory not enough:%d!\n", (int)sizeof(SDecodeContext));
    }
    // close files
    fclose(InputFp);
    delete pContext;
    return nResult;
    
}

/////////////////////////////////////////////////////////////////////////////////
// debug code

#ifdef _DEBUG
extern "C" __declspec(dllexport)
void WINAPI DebugProc(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
{
	char szBuf[300];
	szBuf[0] = '\0';
	LPCSTR pszIn = lpszCmdLine;
	int nRes = MpegDecoder(pszIn, "out.wav", 1, szBuf, 300, NULL);
	if (nRes != 0)
	{
		if (szBuf[0] == '\0')
			sprintf(szBuf, "error = %d", nRes);
		::MessageBox(hWnd, szBuf, NULL, MB_OK);
	}
}
#endif	// _DEBUG
